// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Link-Level implementation controlling the eSPI bus state.
// espi_rx_shifter handles the command phase.
// espi_tx_shifter handles the response phase.
// espi_registers configures the interface, drives alerts, and reports errors.
// Packet boundaries and sampled/drive timer are output to the rest of the slave.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

`timescale 1 ps / 1 ps

module espi_condt_det
  (
   // External I/Os
   input              espi_cs_n,
   input              espi_clk,
   input              espi_reset_n,
   input [3:0]        espi_data_in,
   output logic [3:0] espi_data_out,
   output logic       espi_alert_n,

   // espi_rx_shifter
   input              rx_detect_command,
   input              rx_detect_crc,
   input              detect_rx_idle,
   output logic [7:0] rxshift_data_nxt2,

   // espi_tx_shifter
   input              tx_gen_crc,
   input              detect_tx_idle,
   input [7:0]        tx_dataout_byte,
   output logic       trigger_output,

   // espi_registers
   input              update_status_reg,
   input              opendrain_alert,
   input              alert_mode,
   input [1:0]        io_mode,
   output logic       espi_cs_n_nxt2, // eSPI CS_N delayed by two clks
   output logic       invalid_cs_deassertion, // Unexpected deassertion of CS_N

   // espi_cmd_det
   input              posted_cycle,

   // espi_slave
   input              error_condition1,

   // Broadly used outputs
   output logic       start_det, // start of packet pulse
   output logic       stop_det, // end of packet pulse
   output logic       spiclk_cnt_done_lh, // eSPI CLK sampled low-to-high
   output logic       spiclk_cnt_done_hl, // eSPI CLK sampled high-to-low

   // Globals
   input              clk,
   input              reset_n
   );

    // clk domain
    logic       espi_cs_n_nxt;
    logic       espi_cs_n_nxt3;
    logic       espi_cs_n_nxt4;
    logic [7:0] rxshift_data_nxt;
    logic       spiclk_cnt_done_nxt2;
    logic [3:0] spiclk_cnt_loadvalue;
    logic       spiclk_cnt_done_nxt;
    logic       spiclk_cnt_done_nxt3;
    logic       spiclk_cnt_done_nxt4;
    logic       posspiclk_cnt_done_nxt;
    logic       posspiclk_cnt_done_nxt2;
    logic       posspiclk_cnt_done_nxt3;
    logic       posspiclk_cnt_done_nxt4;

    // espi_clk domain
    logic       rx_detect_command_nxt;
    logic       rx_detect_command_nxt2;
    logic       trigger_input;
    logic [7:0] rxshift_data;
    logic       rx_detect_crc_nxt;
    logic       rx_detect_crc_nxt2;
    logic       trigger_tar;
    logic       trigger_tar_spistg1;
    logic       trigger_tar_spistg2;
    logic       detect_rx_idle_nxt;
    logic       detect_rx_idle_nxt2;
    logic       start_trigger_output;
    logic       load_first_txshift_data;
    logic [7:0] tx_dataout_byte_nxt;
    logic [7:0] tx_dataout_byte_nxt2;
    logic [7:0] txshift_data;
    logic       trigger_txshift_data;
    logic       tx_gen_crc_nxt;
    logic       tx_gen_crc_nxt2;
    logic       stop_trigger_output;
    logic       trigger_output_high;
    logic       detect_tx_idle_nxt;
    logic       detect_tx_idle_nxt2;
    logic       error_condition1_nxt;
    logic       error_condition1_nxt2;
    logic       spiclk_cnt_done;
    logic [3:0] spiclk_cnt;
    logic       posspiclk_cnt_done;
    logic [3:0] shiftvalue;
    logic       trigger_alert;

    // Straddles clk/espi_clk domains but considered static as they only
    // change when espi_cs_n is deasserted and therefore espi_clk is off.
    logic       mono_io_mode;
    logic       dual_io_mode;
    logic       quad_io_mode;


    assign espi_data_out[0] = trigger_output ? (io_mode[0] ? txshift_data[6] :
                                                io_mode[1] ? txshift_data[4] :
                                                1'bz) :
                              trigger_output_high && io_mode[0] ? 1'b1 :
                              1'bz;

    assign espi_data_out[1] = trigger_output ? (io_mode[1] ? txshift_data[5] :
                                                txshift_data[7]) :
                              trigger_output_high ? 1'b1 :
                              trigger_alert && !alert_mode ? 1'b0 :
                              1'bz;

    assign espi_data_out[2] = trigger_output ? (io_mode[1] ? txshift_data[6] :
                                                1'bz) :
                              trigger_output_high && io_mode[1] ? 1'b1 :
                              1'bz;

    assign espi_data_out[3] = trigger_output ? (io_mode[1] ? txshift_data[7] :
                                                1'bz) :
                              trigger_output_high && io_mode[1] ? 1'b1 :
                              1'bz;

    assign espi_alert_n = ~espi_reset_n ? 1'bz :
                          trigger_alert ? 1'b0 :
                          (opendrain_alert ? 1'bz :
                           1'b1);

    assign trigger_alert = espi_cs_n && espi_cs_n_nxt4 && update_status_reg;        // to meet tSHAA by ANDing with espi_cs_n_nxt4, ANDing with espi_cs_n so that Alert wont happen after espi_cs_n is low

    assign start_det = espi_cs_n_nxt3 & ~espi_cs_n_nxt2;
    assign stop_det = ~espi_cs_n_nxt3 & espi_cs_n_nxt2;
    assign spiclk_cnt_done_lh = ~posspiclk_cnt_done_nxt4 & posspiclk_cnt_done_nxt3;
    assign spiclk_cnt_done_hl = spiclk_cnt_done_nxt4 & ~spiclk_cnt_done_nxt3;

    //----------------------------------- IP clock domain -----------------------------------------
    // SPI clock signal to IP clock signal syncronizer
    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            espi_cs_n_nxt           <= 1'b1;
            espi_cs_n_nxt2          <= 1'b1;
            espi_cs_n_nxt3          <= 1'b1;
            espi_cs_n_nxt4          <= 1'b1;
            spiclk_cnt_done_nxt     <= 1'b0;
            spiclk_cnt_done_nxt2    <= 1'b0;
            spiclk_cnt_done_nxt3    <= 1'b0;
            spiclk_cnt_done_nxt4    <= 1'b0;
            posspiclk_cnt_done_nxt  <= 1'b0;
            posspiclk_cnt_done_nxt2 <= 1'b0;
            posspiclk_cnt_done_nxt3 <= 1'b0;
            posspiclk_cnt_done_nxt4 <= 1'b0;
            rxshift_data_nxt        <= 8'd0;
            rxshift_data_nxt2       <= 8'd0;
        end // if (!reset_n)
        else begin
            espi_cs_n_nxt           <= espi_cs_n;
            espi_cs_n_nxt2          <= espi_cs_n_nxt;
            espi_cs_n_nxt3          <= espi_cs_n_nxt2;
            espi_cs_n_nxt4          <= espi_cs_n_nxt3;
            spiclk_cnt_done_nxt     <= spiclk_cnt_done;
            spiclk_cnt_done_nxt2    <= spiclk_cnt_done_nxt;
            spiclk_cnt_done_nxt3    <= spiclk_cnt_done_nxt2;
            spiclk_cnt_done_nxt4    <= spiclk_cnt_done_nxt3;
            posspiclk_cnt_done_nxt  <= posspiclk_cnt_done;
            posspiclk_cnt_done_nxt2 <= posspiclk_cnt_done_nxt;
            posspiclk_cnt_done_nxt3 <= posspiclk_cnt_done_nxt2;
            posspiclk_cnt_done_nxt4 <= posspiclk_cnt_done_nxt3;
            rxshift_data_nxt        <= rxshift_data;
            rxshift_data_nxt2       <= rxshift_data_nxt;
        end // else: !if(!reset_n)
    end // always @ (posedge clk or negedge reset_n)

    always @(posedge clk or negedge reset_n) begin
        if (!reset_n) begin
            invalid_cs_deassertion <= 1'b0;
        end
        else begin
            invalid_cs_deassertion <= espi_cs_n_nxt2 & (~detect_tx_idle | ~detect_rx_idle);
        end
    end

    //----------------------------------- SPI clock domain -----------------------------------------
    // IP clock signal to SPI clock signal syncronizer
    always @(negedge espi_clk or negedge espi_reset_n) begin
        if (!espi_reset_n) begin
            rx_detect_crc_nxt      <= 1'b0;
            rx_detect_crc_nxt2     <= 1'b0;
            detect_rx_idle_nxt     <= 1'b0;
            detect_rx_idle_nxt2    <= 1'b0;
            tx_gen_crc_nxt         <= 1'b0;
            tx_gen_crc_nxt2        <= 1'b0;
            detect_tx_idle_nxt     <= 1'b0;
            detect_tx_idle_nxt2    <= 1'b0;
            rx_detect_command_nxt  <= 1'b0;
            rx_detect_command_nxt2 <= 1'b0;
            error_condition1_nxt   <= 1'b0;
            error_condition1_nxt2  <= 1'b0;
        end // if (!espi_reset_n)
        else begin
            rx_detect_crc_nxt      <= rx_detect_crc;
            rx_detect_crc_nxt2     <= rx_detect_crc_nxt;
            detect_rx_idle_nxt     <= detect_rx_idle;
            detect_rx_idle_nxt2    <= detect_rx_idle_nxt;
            tx_gen_crc_nxt         <= tx_gen_crc;
            tx_gen_crc_nxt2        <= tx_gen_crc_nxt;
            detect_tx_idle_nxt     <= detect_tx_idle;
            detect_tx_idle_nxt2    <= detect_tx_idle_nxt;
            rx_detect_command_nxt  <= rx_detect_command;
            rx_detect_command_nxt2 <= rx_detect_command_nxt;
            error_condition1_nxt   <= error_condition1;
            error_condition1_nxt2  <= error_condition1_nxt;
        end // else: !if(!espi_reset_n)
    end // always @ (negedge espi_clk or negedge espi_reset_n)

    always @(posedge espi_clk or negedge espi_reset_n) begin
        if (!espi_reset_n) begin
            tx_dataout_byte_nxt  <= 8'h0;
            tx_dataout_byte_nxt2 <= 8'h0;
        end
        else begin
            tx_dataout_byte_nxt  <= tx_dataout_byte;
            tx_dataout_byte_nxt2 <= tx_dataout_byte_nxt;
        end
    end

    always @(posedge espi_clk or negedge espi_reset_n) begin
        if (!espi_reset_n) begin
            posspiclk_cnt_done <= 1'b0;
        end
        else begin
            posspiclk_cnt_done <= spiclk_cnt_done;
        end
    end

    // Drive espi_data_out high at the end of the response phase
    always @(negedge espi_clk or posedge espi_cs_n) begin
        if (espi_cs_n) begin
            trigger_output_high <= 1'b0;
        end
        else begin
            if (trigger_input) begin
                trigger_output_high <= 1'b0;
            end
            else if (trigger_output) begin
                trigger_output_high <= 1'b1;
            end
        end // else: !if(espi_cs_n)
    end // always @ (negedge espi_clk or posedge espi_cs_n)

    // Tri-state espi_data_out during command phase
    always @(negedge espi_clk or posedge espi_cs_n) begin
        if (espi_cs_n) begin
            trigger_input <= 1'b0;
        end
        else begin
            if (rx_detect_command_nxt2) begin
                trigger_input <= 1'b1;
            end
            else if (trigger_tar || detect_rx_idle_nxt2) begin
                trigger_input <= 1'b0;
            end
        end // else: !if(espi_cs_n)
    end // always @ (negedge espi_clk or posedge espi_cs_n)

    // Shift data in
    always @(posedge espi_clk or negedge espi_reset_n) begin
        if(!espi_reset_n) begin
            rxshift_data <= 8'h0;
        end
        else begin
            case (1)
              mono_io_mode: rxshift_data <= {rxshift_data[6:0], espi_data_in[0]};
              dual_io_mode: rxshift_data <= {rxshift_data[5:0], espi_data_in[1:0]};
              quad_io_mode: rxshift_data <= {rxshift_data[3:0], espi_data_in[3:0]};
            endcase // case (1)
        end
    end // always @ (posedge espi_clk or negedge espi_reset_n)

    // Shift data out
    always @(negedge espi_clk or negedge espi_reset_n) begin
        if (!espi_reset_n) begin
            txshift_data <= {8{1'b0}};
        end
        else begin
            if (load_first_txshift_data || trigger_txshift_data) begin
                txshift_data <= quad_io_mode ? tx_dataout_byte_nxt :
                                tx_dataout_byte_nxt2;
            end
            else if (trigger_output) begin
                txshift_data <= txshift_data << shiftvalue;
            end
        end // else: !if(!espi_reset_n)
    end // always @ (negedge espi_clk or negedge espi_reset_n)

    assign trigger_txshift_data = trigger_output & spiclk_cnt_done;

    assign load_first_txshift_data = mono_io_mode ? trigger_tar_spistg1 :
                                     dual_io_mode ? trigger_tar_spistg1 :
                                     quad_io_mode ? trigger_tar :
                                     1'h1;

    always @(negedge espi_clk or posedge espi_cs_n) begin
        if (espi_cs_n) begin
            trigger_output <= 1'b0;
        end
        else begin
            if (start_trigger_output) begin
                trigger_output <= 1'b1;
            end
            else if (stop_trigger_output || (error_condition1_nxt2 && spiclk_cnt_done)) begin
                // during error condition 1, slave doesn't need to drive response phase
                trigger_output <= 1'b0;
            end
        end // else: !if(espi_cs_n)
    end // always @ (negedge espi_clk or posedge espi_cs_n)

    always @(posedge espi_clk or negedge espi_reset_n) begin
        if(!espi_reset_n) begin
            stop_trigger_output <= 1'b0;
        end
        else begin
            stop_trigger_output <= quad_io_mode ? trigger_output & tx_gen_crc_nxt2 & spiclk_cnt_done :
                                   detect_tx_idle_nxt2 & trigger_output & spiclk_cnt_done;
        end
    end // always @ (posedge espi_clk or negedge espi_reset_n)

    // Mark byte-time complete
    always @(negedge espi_clk or posedge espi_cs_n) begin
        if (espi_cs_n) begin
            spiclk_cnt_done <= 1'b0;
        end
        else begin
            case (1)
              mono_io_mode: spiclk_cnt_done <= spiclk_cnt == 4'h6;
              dual_io_mode: spiclk_cnt_done <= spiclk_cnt == 4'h4;
              quad_io_mode: spiclk_cnt_done <= spiclk_cnt == 4'h0;
            endcase // case (1)
        end
    end // always @ (negedge espi_clk or posedge espi_cs_n)

    // Count byte-time clocks
    always @(negedge espi_clk or posedge espi_cs_n) begin
        if (espi_cs_n) begin
            spiclk_cnt <= 4'h0;
        end
        else begin
            if (spiclk_cnt_done || start_trigger_output) begin
                spiclk_cnt <= 4'h0;
            end
            else if (~espi_cs_n) begin
                spiclk_cnt <= spiclk_cnt + shiftvalue;
            end
        end // else: !if(espi_cs_n)
    end // always @ (negedge espi_clk or posedge espi_cs_n)

    assign start_trigger_output = mono_io_mode ? trigger_tar_spistg2 :
                                  dual_io_mode ? trigger_tar_spistg2 :
                                  quad_io_mode ? trigger_tar :
                                  1'h1;

    // TAR is 2 clock cycles, uses this register signal to generate the stage
    always @(negedge espi_clk or negedge espi_reset_n) begin
        if (!espi_reset_n) begin
            trigger_tar_spistg1 <= 1'b0;
            trigger_tar_spistg2 <= 1'b0;
        end
        else begin
            trigger_tar_spistg1 <= trigger_tar;
            trigger_tar_spistg2 <= trigger_tar_spistg1;
        end
    end

    assign trigger_tar = rx_detect_crc_nxt2 & spiclk_cnt_done;

    assign shiftvalue = mono_io_mode ? 4'h1 :
                        dual_io_mode ? 4'h2 :
                        quad_io_mode ? 4'h4 : 4'h1;

    assign mono_io_mode = io_mode == 2'b00;
    assign dual_io_mode = io_mode == 2'b01;
    assign quad_io_mode = io_mode == 2'b10;


endmodule // espi_condt_det
